package xman.mssh.hibernate.service;

import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.io.Serializable;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;

import javax.persistence.EmbeddedId;
import javax.persistence.Entity;
import javax.persistence.EntityManager;
import javax.persistence.Id;
import javax.persistence.Query;

import org.hibernate.Session;
import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

/**
 * Copyright (c) 2012, 北京卡拉卡尔科技股份有限公司 All rights reserved.
 * @description 持久化数据业务的通用泛型接口的JPA规范实现类，提高代码的重用性
 * @version 1.0
 * @since tyqk1.0
 * @author shiliufu(liufu.shi@karakal.com.cn)
 * @date 2012-3-6
 */

// @SuppressWarnings("unchecked")
// @Transactional
// @Repository("genericPersistServiceBean")
public class GenericPersistServiceBean {
    // @PersistenceContext
    protected EntityManager em;
    
    // 为了日志记录提供数据

    public void setEntityString(Object entityString) {
        try {
            // ServletActionContext.getRequest().getSession().setAttribute("loggerObj", entityString);
        } catch (Exception e) {
        }
    }

    public void clear() {
        // 清除一级缓存的所有缓存数据
        em.clear();
    }

    public void flush() {
        // 强行刷新一级缓存，对象会持久化到数据库
        em.flush();
    }

    public void persist(Object entity) {
        // 实体持久化到数据库
        em.persist(entity);
        setEntityString(entity);
    }

    public void update(Object entity) {
        // 更新实体
        em.merge(entity);
        setEntityString(entity.toString());
    }

    public void updateWithoutServletActionContext(Object entity) {
        em.merge(entity);
    }

    public void merge(Object entity) {
        update(entity);
        setEntityString(entity.toString());
    }
    
    public <T> void delete(Class<T> entityClass, Integer entityid) {
        // 根据对象标识符获取对象的引用，然后删除
        em.remove(em.getReference(entityClass, entityid));
        setEntityString("从" + entityClass.getName() + "删除了标识为" + entityid + "对象");
    }

    public <T> void delete(Class<T> entityClass, Serializable... entityids) {
        for (Object id : entityids) {
            // 根据对象标识符获取对象的引用，然后删除
            em.remove(em.getReference(entityClass, id));
            setEntityString("从" + entityClass.getName() + "删除了标识为" + entityids + "对象");
        }
    }

    @Transactional(readOnly = true, propagation = Propagation.NOT_SUPPORTED)
    public <T> T queryById(Class<T> entityClass, Serializable entityId) {
        // 检查entityId参数的有效性，以防空指针异常
        if (entityId == null)
            throw new RuntimeException(entityClass.getName() + ":传入的实体id不能为空");
        return em.find(entityClass, entityId);
    }

    @Transactional(readOnly = true, propagation = Propagation.NOT_SUPPORTED)
    public <T> T queryFirstByWhere(Class<T> entityClass, String wherejpql, Object[] queryParams) {
        Query query = null;
        ArrayList<T> retList = null;
        // 组装Query查询对象
        query = em.createQuery("select o from " + getEntityName(entityClass) + " o "
                + (wherejpql == null || "".equals(wherejpql.trim()) ? "" : "where " + wherejpql));
        // 对queryString的占位符?进行动态绑定
        setQueryParams(query, queryParams);
        retList = (ArrayList<T>) query.getResultList();
        // 从无序的集合中取出第一条记录返回
        return retList.size() == 0 ? null : retList.get(0);
    }

    @Transactional(readOnly = true, propagation = Propagation.NOT_SUPPORTED)
    public <T> T queryFirstOrderByWhere(Class<T> entityClass, String wherejpql, Object[] queryParams,
            LinkedHashMap<String, OrderbyEnumType> orderby) {
        Query query = null;
        ArrayList<T> retList = null;
        // 组装Query查询对象
        query = em.createQuery("select o from " + getEntityName(entityClass) + " o "
                + (wherejpql == null || "".equals(wherejpql.trim()) ? "" : "where " + wherejpql)
                + buildOrderby(entityClass, orderby));
        // 对queryString的占位符?进行动态绑定
        setQueryParams(query, queryParams);
        retList = (ArrayList<T>) query.getResultList();
        // 从有序的集合中取出第一条记录返回
        return retList.size() == 0 ? null : retList.get(0);
    }

    @Transactional(readOnly = true, propagation = Propagation.NOT_SUPPORTED)
    public <T> long getCount(Class<T> entityClass) {
        // 返回满足条件的记录数据的个数
        return (Long) em.createQuery(
                "select count(" + getCountField(entityClass) + ") from " + getEntityName(entityClass) + " o")
                .getSingleResult();
    }

    public <T> long getCountByWhere(Class<T> entityClass, String wherejpql, Object[] queryParams) {
        Query query = null;
        query = em.createQuery("select count(" + getCountField(entityClass) + ") from " + getEntityName(entityClass)
                + " o " + (wherejpql == null || "".equals(wherejpql.trim()) ? "" : "where " + wherejpql));
        setQueryParams(query, queryParams);
        return (Long) query.getSingleResult();
    }

    public <T> ArrayList<T> queryAll(Class<T> entityClass) {
        return (ArrayList<T>) em.createQuery(" select o from " + getEntityName(entityClass) + " o ").getResultList();
    }

    public <T> ArrayList<T> queryAllByWhere(Class<T> entityClass, String wherejpql, Object[] queryParams) {
        Query query = null;
        ArrayList<T> list = null;
        query = em.createQuery("select o from " + getEntityName(entityClass) + " o "
                + (wherejpql == null || "".equals(wherejpql.trim()) ? "" : "where " + wherejpql));
        setQueryParams(query, queryParams);
        list = (ArrayList<T>) query.getResultList();
        return list;
    }

    @Transactional(readOnly = true, propagation = Propagation.NOT_SUPPORTED)
    public <T> QueryResult<T> getScrollData(Class<T> entityClass, int firstindex, int maxresult, String wherejpql,
            Object[] queryParams, LinkedHashMap<String, OrderbyEnumType> orderby) {
        QueryResult<T> qr = new QueryResult<T>();
        String entityname = getEntityName(entityClass);
        // 组装Query查询对象
        Query query = em.createQuery("select o from " + entityname + " o "
                + (wherejpql == null || "".equals(wherejpql.trim()) ? "" : "where " + wherejpql)
                + buildOrderby(entityClass, orderby));
        // 对queryString的占位符?进行动态绑定
        setQueryParams(query, queryParams);
        if (firstindex != -1 && maxresult != -1)
            query.setFirstResult(firstindex).setMaxResults(maxresult);
        // 获取满足分页内的数据集，并写入QueryResult，统一页面分页显示方式
        qr.setResultlist(query.getResultList());
        // 组装获取总记录数的Query查询对象
        query = em.createQuery("select count(" + getCountField(entityClass) + ") from " + entityname + " o "
                + (wherejpql == null || "".equals(wherejpql.trim()) ? "" : "where " + wherejpql));
        // 对queryString的占位符?进行动态绑定
        setQueryParams(query, queryParams);
        // 用单一结果集获取符合条件的总记录数
        qr.setTotalrecord((Long) query.getSingleResult());
        return qr;
    }

    @Transactional(readOnly = true, propagation = Propagation.NOT_SUPPORTED)
    public <T> QueryResult<T> getScrollData(Class<T> entityClass, int firstindex, int maxresult,
            LinkedHashMap<String, OrderbyEnumType> orderby) {
        return getScrollData(entityClass, firstindex, maxresult, null, null, orderby);
    }

    @Transactional(readOnly = true, propagation = Propagation.NOT_SUPPORTED)
    public <T> QueryResult<T> getScrollData(Class<T> entityClass, int firstindex, int maxresult, String wherejpql,
            Object[] queryParams) {
        return getScrollData(entityClass, firstindex, maxresult, wherejpql, queryParams, null);
    }

    @Transactional(readOnly = true, propagation = Propagation.NOT_SUPPORTED)
    public <T> QueryResult<T> getScrollData(Class<T> entityClass, int firstindex, int maxresult) {
        return getScrollData(entityClass, firstindex, maxresult, null, null, null);
    }

    @Transactional(readOnly = true, propagation = Propagation.NOT_SUPPORTED)
    public <T> QueryResult<T> getScrollData(Class<T> entityClass) {
        return getScrollData(entityClass, -1, -1);
    }

    protected static void setQueryParams(Query query, Object[] queryParams) {
        if (queryParams != null && queryParams.length > 0) {
            for (int i = 0; i < queryParams.length; i++) {
                query.setParameter(i + 1, queryParams[i]);
            }
        }
    }

    /**
     * 组装order by语句
     * @param orderby
     * @return
     */
    protected static <T> String buildOrderby(Class<T> entityClass, LinkedHashMap<String, OrderbyEnumType> orderby) {
        StringBuffer orderbyql = new StringBuffer("");
        if (orderby != null && orderby.size() > 0) {
            orderbyql.append(" order by ");
            for (String key : orderby.keySet()) {
                if (key.contains("o.")) {
                    orderbyql.append(key).append(" ").append(orderby.get(key).getOrderbyTypeString()).append(",");
                } else {
                    orderbyql.append("o.").append(key).append(" ").append(orderby.get(key).getOrderbyTypeString())
                            .append(",");
                }
            }
            // 删除最后多余的,
            orderbyql.deleteCharAt(orderbyql.length() - 1);
            return orderbyql.toString();
        } else {
            return defaultOrderby(entityClass);
        }

    }

    /**
     * 组装默认order by语句
     * @param orderby
     * @return
     */
    protected static <T> String defaultOrderby(Class<T> entityClass) {
        // 通过反射获取类上的所有方法
        Method[] methods = entityClass.getMethods();
        String fieldName = null;
        StringBuffer orderbyql = new StringBuffer("");
        for (Method method : methods) {
            // 判断方法上面是否有@Id注解，如果有是表明是实体类主键属性所对应的get方法
            if (method.isAnnotationPresent(Id.class)) {
                // 获取方法所对应的成员属性名
                fieldName = Introspector.decapitalize(method.getName().substring(3));
                break;
            }
        }
        if (null != fieldName && !"".equals(fieldName)) {
            // 用主键属性组装排序串
            orderbyql.append(" order by o." + fieldName + " desc");
        }
        return orderbyql.toString();
    }

    /**
     * 获取实体的名称
     * @param <E>
     * @param clazz 实体类
     * @return
     */
    protected static <E> String getEntityName(Class<E> clazz) {
        // 获取简单类名
        String entityname = clazz.getSimpleName();
        // 从类字节码上获取类上的@Entity注解
        Entity entity = clazz.getAnnotation(Entity.class);
        if (entity.name() != null && !"".equals(entity.name())) {
            // 从@Entity注解上获取name属性的值
            entityname = entity.name();
        }
        return entityname;
    }

    /**
     * 获取统计属性,该方法是为了解决hibernate解析联合主键select count(o) from Xxx o语句BUG而增加, hibernate对此jpql解析后的sql为select
     * count(field1,field2,...),显示使用count()统计多个字段是错误的
     * @param <E>
     * @param clazz
     * @return
     */
    protected static <E> String getCountField(Class<E> clazz) {
        String out = "o";
        try {
            PropertyDescriptor[] propertyDescriptors = Introspector.getBeanInfo(clazz).getPropertyDescriptors();
            for (PropertyDescriptor propertydesc : propertyDescriptors) {
                Method method = propertydesc.getReadMethod();
                if (method != null && method.isAnnotationPresent(EmbeddedId.class)) {
                    PropertyDescriptor[] ps = Introspector.getBeanInfo(propertydesc.getPropertyType())
                            .getPropertyDescriptors();
                    out = "o." + propertydesc.getName() + "."
                            + (!ps[1].getName().equals("class") ? ps[1].getName() : ps[0].getName());
                    break;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return out;
    }

    public Connection getDBConnection() {
        // 从JPA环境中获取数据源对象
        try {
            Session session = (Session) em.getDelegate();
            SessionFactoryImplementor sfi = (SessionFactoryImplementor) session.getSessionFactory();
            ConnectionProvider cp = sfi.getConnectionProvider();
            Connection conn = cp.getConnection();
            return conn;
        } catch (SQLException e) {
            throw new RuntimeException(this.getClass().getName() + ":getDBConnection()获取数据库连接失败");
        }
    }

    /**
     * 自定义JPQL语句查询
     * @param sqlStatements 查询语句SQL块，使用JPQL方式，以对象、属性查询
     * @param queryParams 占位符，动态绑定参数值
     * @return 返回不是对象的数据列表
     */
    @SuppressWarnings("rawtypes")
    public List getScrollDataByJPQL(String sqlStatements, Object[] queryParams) {
        Query query = em.createQuery(sqlStatements);
        setQueryParams(query, queryParams);
        return query.getResultList();
    }

    /**
     * 自定义JPQL语句查询
     * @param sqlStatements 查询语句SQL块，使用标准SQL方式，以对象、属性查询
     * @param queryParams 占位符，动态绑定参数值
     * @return 返回不是对象的数据列表
     */
    @SuppressWarnings("rawtypes")
    public List getScrollDataBySQL(String sqlStatements, Object[] queryParams) {
        Query query = em.createNativeQuery(sqlStatements);
        setQueryParams(query, queryParams);
        return query.getResultList();
    }

    /**
     * 多条件自定义查询功能
     * @param jpql JPQL查询语句块
     * @return
     */
    public <T> QueryResult<T> getScrollData(String jpql, String countjpql, int firstindex, int maxresult,
            LinkedHashMap<String, OrderbyEnumType> orderby) {
        QueryResult<T> qr = new QueryResult<T>();
        // 组装Query查询对象
        Query query = em.createQuery(jpql + buildOrderby(null, orderby));
        if (firstindex != -1 && maxresult != -1)
            query.setFirstResult(firstindex).setMaxResults(maxresult);
        // 获取满足分页内的数据集，并写入QueryResult，统一页面分页显示方式
        qr.setResultlist(query.getResultList());
        // 组装获取总记录数的Query查询对象
        query = em.createQuery(countjpql);
        // 用单一结果集获取符合条件的总记录数
        qr.setTotalrecord((Long) query.getSingleResult());
        return qr;
    }
}
